package com.zjsru.oneDay202211;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: CookLee
 * @Date: 2022/11/30
 * 最大频率栈
 *
 * 输入：
 * ["FreqStack","push","push","push","push","push","push","pop","pop","pop","pop"],
 * [[],[5],[7],[5],[7],[4],[5],[],[],[],[]]
 * 输出：[null,null,null,null,null,null,null,5,7,5,4]
 * 解释：
 * FreqStack = new FreqStack();
 * freqStack.push (5);//堆栈为 [5]
 * freqStack.push (7);//堆栈是 [5,7]
 * freqStack.push (5);//堆栈是 [5,7,5]
 * freqStack.push (7);//堆栈是 [5,7,5,7]
 * freqStack.push (4);//堆栈是 [5,7,5,7,4]
 * freqStack.push (5);//堆栈是 [5,7,5,7,4,5]
 * freqStack.pop ();//返回 5 ，因为 5 出现频率最高。堆栈变成 [5,7,5,7,4]。
 * freqStack.pop ();//返回 7 ，因为 5 和 7 出现频率最高，但7最接近顶部。堆栈变成 [5,7,5,4]。
 * freqStack.pop ();//返回 5 ，因为 5 出现频率最高。堆栈变成 [5,7,4]。
 * freqStack.pop ();//返回 4 ，因为 4, 5 和 7 出现频率最高，但 4 是最接近顶部的。堆栈变成 [5,7]。
 */
public class FreqStack {
    
    public FreqStack() {
    
    }
    
    /**
     * 以「出现次数 c」为键，以「出现次数均为 c 的元素序列」为值
     */
    Map<Integer, List<Integer>> map = new HashMap<>();
    
    /**
     * 记录某个数值的出现次数
     */
    Map<Integer, Integer> cnts = new HashMap<>();
    
    /**
     * 获取最大出现频数
     */
    int max;
    
    /**
     * 记录
     * */
    public void push(int val) {
        //将val记录到map中，次数从0开始叠加
        cnts.put(val, cnts.getOrDefault(val, 0) + 1);
        //记录val值出现的次数，即表示map的层级
        int c = cnts.get(val);
        //每层map的不允许出现相同值，重复值会随着c存放到对应层级的list中去，如果不存在list则新建
        List<Integer> list = map.getOrDefault(c, new ArrayList<>());
        list.add(val);
        //更新对应的map
        map.put(c, list);
        //获取最大出现频数
        max = Math.max(max, c);
    }
    
    /**
     * 弹出元素
     * */
    public int pop() {
        //获取最高频率的元素集合
        List<Integer> list = map.get(max);
        //获取弹出的最后一个元素值
        int ans = list.remove(list.size() - 1);
        //根据弹出元素去找该元素出现的次数并-1，更新map
        cnts.put(ans, cnts.get(ans) - 1);
        // 当list的长度为0，代表该层级下的元素值已经全部弹出，将max-1
        if (list.size() == 0) {
            max--;
        }
        return ans;
    }
}
